(function () {
    return {
        create: function ($, canvas, fnGetDC, fnGetScale, fnDocSizeChange) {
			var templ = {};

			//常量
			templ.HANDLE_WIDTH = 7; //句柄大小
			
			templ.TOLERANCE_MOVE = 2; //允许的误差大小（鼠标拖动时）
			templ.TOLERANCE_SEL = 9; //允许的误差大小（选中元素时）
			

			//工具函数
			function scale(val) {
				return canvas.scale(val, fnGetScale())
			}

			function ptInLine(pt, start, end) {
				var delta = templ.TOLERANCE_SEL;

				//pt必须在对角线所在矩形内
				var x1 = Math.min(start.x, end.x);
				var x2 = Math.max(start.x, end.x);
				var y1 = Math.min(start.y, end.y);
				var y2 = Math.max(start.y, end.y);
				if (pt.x < x1 - delta / 2 || pt.x > x2 + delta / 2) return false;
				if (pt.y < y1 - delta / 2 || pt.y > y2 + delta / 2) return false;

				//pt在容差范围内必须在直线上

				if (start.x === end.x) {
					if (Math.abs(pt.x - start.x) < delta) {
						var y1 = Math.min(start.y, end.y);
						var y2 = Math.max(start.y, end.y);
						if (pt.y >= y1 && pt.y <= y2) return true;
					}
				} else if (start.y === end.y) {
					if (Math.abs(pt.y - start.y) < delta) {
						var x1 = Math.min(start.x, end.x);
						var x2 = Math.max(start.x, end.x);

						if (pt.x >= x1 && pt.x <= x2) return true;
					}
				} else {
					var dx = (start.x - end.x);
					var dy = (start.y - end.y);
					var k = 1.0 * dy / dx;
					var b = start.y - k * start.x;

					var cy = Math.abs(pt.x * k + b - pt.y);

					if (cy < delta) return true;
				}
				return false;
			}

			function ptInRect(pt, rect) {
				return pt.x >= rect.x && pt.x <= rect.x + rect.width && pt.y >= rect.y && pt.y <= rect.y + rect.height;
			}

			function getDistance(pt1, pt2) {
				var dx = Math.abs(pt1.x - pt2.x);
				var dy = Math.abs(pt1.y - pt2.y);
				return Math.sqrt(dx * dx + dy * dy);
			}
			
			//ID生成器
			templ.IDMaker = function () {
				return {
					id: 0,
					New: function () {
						return ++this.id;
					}
				}
			};

			//环节类型
			templ.ShapeType = {
				TEXT: 0,
				IMAGE: 1,
                MINIQR: 2
			};
			templ.ShapeTypeName = ["文本", "图片","小程序码"];

			function _assign(objTo, objFrom) {
				if (objFrom && $.isObject(objFrom)) {
					$.each(objTo, function (v, k) {
						if (k in objFrom) {
							objTo[k] = objFrom[k]
						}
					});
				}

				return objTo;
			}

			templ.shapeBase = {
				id: 0,
				position: null
			};

            var text_bindtype_arr = ["不绑定","用户昵称", "会员id", "产品名称", "产品简介", "产品卖价", "产品市场价"];
			templ.shapeText = function (o) {
				var p = $.extend({}, templ.shapeBase, {
					type: templ.ShapeType.TEXT,
                    text: "",
                    fontsize: 10,
                    fontname: "",
                    fontcolor:"",
                    isbold: 0,
                    isitalic:0,
                    fontcolor: "",
                    halign:0,
                    bindtype:0
				});
				return _assign(p, o);
			};

            var image_bindtype_arr = ["不绑定", "用户头像", "产品主图"];

			templ.shapeImage = function (o) {
				var p = $.extend({}, templ.shapeBase, {
                    type: templ.ShapeType.IMAGE,
                    url: "",
                    isrounded:false,
                    bindtype:0
				});
				return _assign(p, o);
            };
            templ.shapeMiniQr = function (o) {
				var p = $.extend({}, templ.shapeBase, {
                    type: templ.ShapeType.MINIQR,
                    isrounded:false,
                    qrtype:0//0:小程序码 1:产品码
				});
				return _assign(p, o);
            };

			
			//文档控制器
			templ.controler = function (doc) {
				return {
					doc: doc,

					onMouseDown: function (e) {
						var me = this;

						var x = e.offsetX-me.doc.getSpace().width;
						var y = e.offsetY-me.doc.getSpace().height;

						var downPos = canvas.point(x, y);
						me.doc.setDownPos(downPos);

						var ht = me.doc.hitTest(downPos);
						if (ht) {
							var downShape = ht.shape;
							var downSide = ht.position;

							if (downSide === "left" || downSide === "right" || downSide === "top" || downSide === "bottom")
								me.doc.setDownHot(ht);
							else {
								me.doc.setDownShape(downShape);
								me.doc.setDownSide(downSide);

								if (!e.shiftKey) {
									if (!me.doc.isSelected(downShape)) {
										me.doc.unselectAll();
									}
								}
								me.doc.select(downShape);
								me.doc.setFocus(downShape);
							}
						} else {
							me.doc.unselectAll();
						}



						me.doc.updateView();
					},
					onMouseMove: function (e) {
						var me = this;

						var x = e.offsetX-me.doc.getSpace().width;
						var y = e.offsetY-me.doc.getSpace().height;

						var movePos = canvas.point(x, y);
						me.doc.setMovePos(movePos);

						if (e.which === 1) {
							var downShape = me.doc.getDownShape();
							if (!downShape) {
								//do nothing
								//暂不实现圈选
							} else {
								var downPos = me.doc.getDownPos();

                                var cx = x - downPos.x;
                                var cy = y - downPos.y;

                                var dx = Math.abs(cx);
                                var dy = Math.abs(cy);

                                if (dx > templ.TOLERANCE_MOVE || dy > templ.TOLERANCE_MOVE) {
                                    me.doc.setIsMoving(true);
                                } else {
                                    //误操作 do nothing
                                }

							}
						}
						//mouse over
		
                        var h = me.doc.hitTest(movePos);
                        if (h) {

                            if (h.position === "body") {
                                me.doc.setOverShape(h.shape);
                            }
                        } else {
                            me.doc.setOverShape(null);
                        }
						
						me.doc.updateView();
					},
					onMouseUp: function (e) {
						var me = this;

						var x = e.offsetX-me.doc.getSpace().width;
						var y = e.offsetY-me.doc.getSpace().height;
						var cx, cy, dx, dy;
						var theScale = fnGetScale();

						var downShape = me.doc.getDownShape();
						if (downShape) {
							var downPos = me.doc.getDownPos();
							var downSide = me.doc.getDownSide();

							cx = x - downPos.x;
							cy = y - downPos.y;

							dx = Math.abs(cx);
							dy = Math.abs(cy);

							if (!(downPos && (dx > templ.TOLERANCE_MOVE || dy > templ.TOLERANCE_MOVE))) {
								//as click
								//do nothing
							} else {
								//as move
								cx /= theScale;
								cy /= theScale;
                        
                                if (downSide == "body") {
                                    $.each(me.doc.getAllSelected(), function (shape) {
                                        me.doc.moveShape(shape, cx, cy);
                                    });
                                } else  {
                                    $.each(me.doc.getAllSelected(), function (shape) {
                                        me.doc.resizeShape(shape, downSide, cx, cy);
                                    });
                                }
                                
							}
						}
						

						me.doc.setDownPos(null);
						me.doc.setDownShape(null);
						me.doc.setDownSide("");
						me.doc.setMovePos(null);
						me.doc.setIsMoving(false);
						me.doc.setOverShape(null);
	
						if (me.downShape ) {
							me.doc.updateView();
						}

					},
					onKeyDown: function (e) {

						if (e.keyCode === 46) {
							//delete
							this.doc.deleteSelected();
						}
						this.doc.updateView();
					},
					onKeyPress: function (e) {

					},
					onKeyUp: function (e) {
						var me = this;

						var code = e.keyCode;
						if (e.shiftKey && code >= 37 && code <= 40) {
							//37 = left 38 = up 39=right 40=down
							var cx = (code === 37 ? -1 : (code === 39 ? 1 : 0));
							var cy = (code === 38 ? -1 : (code === 40 ? 1 : 0));

							var selected = me.doc.getAllSelected();

                            $.each(selected, function (shape) {
                                me.doc.moveShape(shape, cx, cy);
								
							});
							me.doc.updateView();

						}
					},
					RemoveSelections:function () {
						var me = this;
                        me.doc.deleteSelected();
					}
				}
			};

			//文档视图
			templ.view = function () {
				return {
					draw: function (doc) {
						var me = this;

						var dc = fnGetDC();
						if (!dc) return;

						canvas.beginDraw(dc);

						if ($.getConfig().debug) {
							me._draw(doc, dc);
						} else {
							try {
								me._draw(doc, dc);
							} catch (e) {
								$.log("occured when draw workflow,msg:" + e.message)
							}
						}

						canvas.endDraw(dc);
                    },
                    drawPreview: function (doc, dc) {
                        var me = this;
                        canvas.beginDraw(dc);

                        var docSize = doc.getSize();
                        
                        var bgimg = doc.getApp().bgimg;
                        if (bgimg) bgimg = doc.getShapeImage(bgimg);
                        if (bgimg ) {
                            canvas.drawImage(dc, bgimg,
								0, 0, docSize.width, docSize.height,
								0, 0, docSize.width, docSize.height
							);
                        } else {
                            var bgcolor = doc.getApp().bgcolor;
                        
                            var br;
                            if (!bgcolor) {
                                br = canvas.color(255,255,255, 255);
                            } else {
                                br = canvas.color(bgcolor);
                            }

                            canvas.fillRectangle(
                                dc,
                                canvas.brush(br),
                                canvas.rect(0, 0, docSize.width, docSize.height));
                        }
                        
                        $.each(doc.getAllShape(), function (p) {
                            me.drawShape(doc, dc, p)
                        });

						canvas.endDraw(dc);
                    },
					_draw: function (doc, dc) {
						var me = this;
                        me.drawCanvas(doc, dc);
                        
                        var docSpace = doc.getSpace();
                        var docSize = doc.getSize();
                        canvas.save(dc);
                        canvas.translate(dc,docSpace.width, docSpace.height);
                        
                        var bgimg = doc.getApp().bgimg;
                        if (bgimg) bgimg = doc.getShapeImage(bgimg);
                        if (bgimg ) {
                            canvas.drawImage(dc, bgimg,
								0, 0, docSize.width, docSize.height,
								0, 0, docSize.width, docSize.height
							);
                        } else {
                            var bgcolor = doc.getApp().bgcolor;
                        
                            var br;
                            if (!bgcolor) {
                                br = canvas.color(255,255,255, 255);
                            } else {
                                br = canvas.color(bgcolor);
                            }

                            canvas.fillRectangle(
                                dc,
                                canvas.brush(br),
                                canvas.rect(0, 0, docSize.width, docSize.height));
                        }
                        
                        canvas.drawRectangle(
                            dc,
                            canvas.pen(canvas.color(234, 234, 234, 255),1),
                            canvas.rect(0, 0, docSize.width, docSize.height));

                        me.drawOver(doc, dc);
                        $.each(doc.getAllShape(), function (p) {
                            me.drawShape(doc, dc, p)
                        });
                        
                        me.drawHandler(doc, dc);

                        if (doc.getIsMoving() )
                            me.drawMoving(doc, dc);
                        
                        canvas.restore(dc);
					},
					//画整体背景
					drawCanvas: function (doc, dc) {
                        var docSize = doc.getSize();
                        var docSpace = doc.getSpace();
						var w = docSize.width+docSpace.width*2;
                        var h = docSize.height + docSpace.height * 2;
                        if (w < document.body.offsetWidth) w = document.body.offsetWidth;
                        if (h < document.body.offsetHeight) h = document.body.offsetHeight;

						canvas.fillRectangle(
							dc,
							canvas.brush(canvas.color(68, 85, 136, 255)),
							canvas.rect(0, 0, w, h));
						
					},
					//画选中句柄
					drawHandler: function (doc, dc) {
						var focus = doc.getFocus();
						$.each(doc.getAllSelected(), function (shape) {
							var h = doc.getHandle(shape);
							if (h) {
								$.each(h, function (r, side) {

									var bgCr = canvas.color(241, 241, 241, 220);
									var borderCr = canvas.color(80, 80, 80, 220);
									var bgCrActive = canvas.color(250, 86, 16, 220);
									var borderCrActive = canvas.color(198, 63, 4, 220);

									var br = canvas.brush(shape === focus ? bgCrActive : bgCr);
									var pen = canvas.pen(shape === focus ? borderCrActive : borderCr, 1);

									canvas.fillRectangle(dc, br, r);
									canvas.drawRectangle(dc, pen, r);
								})
							}

						})
					},
					drawShape: function (doc, dc, p) {
						
						switch (p.type) {
							case templ.ShapeType.TEXT:
								this.drawShapeText(doc, dc, p);
								break;
							case templ.ShapeType.IMAGE:
								this.drawShapeImage(doc, dc, p);
                                break;
                            case templ.ShapeType.MINIQR:
                                this.drawShapeMiniQr(doc, dc, p);
                                break;
							default:
								break;
						}

					},
					drawShapeText: function (doc, dc, p) {
						var r = p.position;
						

                        var text = p.text;
                        if (p.bindtype) {
                            text = "[["+text_bindtype_arr[p.bindtype]+"]]";
                        }
                        var fontsize = $.toInt(p.fontsize);
                        var fontstyle = canvas.FontStyle.REGULAR;
                        if (p.isbold) fontstyle = fontstyle | canvas.FontStyle.BOLD;
                        if (p.isitalic) fontstyle = fontstyle | canvas.FontStyle.ITALIC;
                        var fnt = canvas.font("宋体", fontsize, fontstyle);
                        if (p.fontcolor)
                            fnt.color = canvas.color(p.fontcolor);
                        else
                            fnt.color = canvas.color(0, 0, 0, 255);
                        var len = text.length;

                        var top = r.y;
                        
                        var lineWidth = 0;
                        var lastSubStrIndex = 0; //每次开始截取的字符串的索引 
                        var halign = $.toInt(p.halign);
                        for (var i = 0; i < len; i++) {
                            var m = canvas.measureText(dc, fnt, text[i]);
                            lineWidth += m.width;
                            if (lineWidth > r.width) {
                                var left = r.x;
                                if (halign === 0) left = r.x;
                                else if (halign === 1) left = r.x+r.width/2-lineWidth/2;
                                else if (halign === 2) left = r.x+r.width-lineWidth;
                                canvas.drawString(
                                    dc,
                                    text.substring(lastSubStrIndex, i),
                                    {x:left,y:top,width:r.width,height:r.height},
                                    fnt,
                                    canvas.TextAlign.NEAR,
                                    canvas.TextAlign.NEAR
                                );
               
                                top += fontsize + 1; 
                                
                                lineWidth = 0;
                                lastSubStrIndex = i;
                            }
                            if (i == len - 1) { //绘制剩余部分                
                                var left = r.x;
                                lineWidth = canvas.measureText(dc, fnt, text.substring(lastSubStrIndex, i+1)).width;
                                if (halign === 0) left = r.x;
                                else if (halign === 1) left = r.x+r.width/2-lineWidth/2;
                                else if (halign === 2) left = r.x+r.width-lineWidth;
                                canvas.drawString(
                                    dc,
                                    text.substring(lastSubStrIndex, i+1),
                                    {x:left,y:top,width:r.width,height:r.height},
                                    fnt,
                                    canvas.TextAlign.NEAR,
                                    canvas.TextAlign.NEAR
                                );
                            }
                        }   
                        
                    },
                    drawShapeImage: function (doc, dc, p) {
                        var r = $.extend({},p.position);
                        
                        /*if (p.bindtype) {
                            var text = "[["+image_bindtype_arr[p.bindtype]+"]]";
                            var br = canvas.brush(canvas.color(222, 222, 222, 180));
                            var pen = canvas.pen(canvas.color(99, 99, 99, 200), 1);
                                
                            canvas.fillRectangle(dc, br, r);
                            canvas.drawRectangle(dc, pen, r);
                            
                            var fnt = canvas.font("宋体", 16, canvas.FontStyle.REGULAR);
                            fnt.color = canvas.color(0, 0, 0, 255);

                            canvas.drawString(
                                dc,
                                text,
                                r,
                                fnt,
                                canvas.TextAlign.CENTER,
                                canvas.TextAlign.CENTER
                            );
                        } else {*/
                            var img = doc.getShapeImage(p.url);
                        if (img) {
                            if (p.isrounded) {
                                dc.save();
                                dc.beginPath();
                                var rd = r.width / 2;
                                dc.arc(r.x+rd,r.y+rd, rd, 0, Math.PI * 2, false);
                                dc.clip();
                                canvas.drawImage(dc, img,
                                    r.x, r.y, r.width,r.width
                                );
                                dc.restore();
                            } else {
                                canvas.drawImage(dc, img,
                                    r.x, r.y, r.width,r.width
                                );
                            }
               
                            } else {
                                var br = canvas.brush(canvas.color(222, 222, 222, 180));
                                var pen = canvas.pen(canvas.color(99, 99, 99, 200), 1);
                                    
                                canvas.fillRectangle(dc, br, r);
                                canvas.drawRectangle(dc, pen, r);
                                
                                var fnt = canvas.font("宋体", 16, canvas.FontStyle.REGULAR);
                                fnt.color = canvas.color(0, 0, 0, 255);

                                canvas.drawString(
                                    dc,
                                    "请上传图片",
                                    r,
                                    fnt,
                                    canvas.TextAlign.CENTER,
                                    canvas.TextAlign.CENTER
                                );
                            }
                        //}

                    },
                    drawShapeMiniQr: function (doc, dc, p) {
                        var r = p.position;
                        var img = doc.getShapeImage(p.url);
                        if (img) {
                            if (p.isrounded) {
                                dc.save();
                                dc.beginPath();
                                var rd = r.width / 2;
                                dc.arc(r.x+rd,r.y+rd, rd, 0, Math.PI * 2, false);
                                dc.clip();
                                canvas.drawImage(dc, img,
                                    r.x, r.y, r.width,r.width
                                );
                                dc.restore();
                            } else {
                                canvas.drawImage(dc, img,
                                    r.x, r.y, r.width,r.width
                                );
                            }
                            
                        }
                        else {
                            var ptc = canvas.point(r.x + r.width / 2, r.y + r.height / 2);
                            var rd = r.width / 2;
                            canvas.fillCircle(dc,canvas.brush(canvas.color(222,222,222, 255)),ptc,rd);
                            canvas.drawCircle(dc,canvas.pen(canvas.color(244,244,244,255),1),ptc,rd);
                        }
                        
                    },
                    	
					drawMoving: function (doc, dc) {
						var me = this;
						if (!doc.movePos || !doc.downPos) return;
						var cx = doc.movePos.x - doc.downPos.x;
                        var cy = doc.movePos.y - doc.downPos.y;
                        
                        var downSide = doc.getDownSide();

						if (Math.abs(cx) > templ.TOLERANCE_MOVE || Math.abs(cy) > templ.TOLERANCE_MOVE) {
							$.each(doc.getAllSelected(), function (shape) {
                                var r = $.extend({}, shape.position);
                                if (downSide == "body") {
                                    r.x += cx;
                                    r.y += cy;
                                } else if (downSide == "LeftTop") {
                                    r.x += cx;
                                    r.y += cy;
                                    r.width -= cx;
                                    r.height -= cy;
                                } else if (downSide == "RightTop") {
                                    r.width += cx;
                                    r.y += cy;
                                    r.height -= cy;
                                } else if (downSide == "LeftBottom") {
                                    r.x += cx;
                                    r.width -= cx;
                                    r.height += cy;
                                } else if (downSide == "RightBottom") {
                                    r.width += cx;
                                    r.height += cy;
                                }
                                

                                var br = canvas.brush(canvas.color(244, 244, 244, 180));
                                var pen = canvas.pen(canvas.color(117, 177, 183, 200), 1);
                                pen.style = canvas.PenStyle.DASH;
                                pen.dash = [7, 2];

                                canvas.fillRectangle(dc, br, r);
                                canvas.drawRectangle(dc, pen, r);
									

							})
						}


					},
					drawOver: function (doc, dc) {
						var overShape = doc.getOverShape();
						if (overShape) {
                            var r = $.extend({},overShape.position);
                            r.width += 4;
                            r.height += 4;
                            r.x -= 2;
                            r.y -= 2;

                            var br = canvas.brush(canvas.color(150, 135, 250, 80));
                            canvas.fillRectangle(dc, br, r);	
						}
					}
				}
			};


			/*
			* 流程文档
				存储工作流数据，比如环节、路由；当数据变化时通过事件通知订阅者
			主要事件：
				事件名称				描述					参数
				-------------------------------------------------------------------------------------------
				shapeCreate			元素创建				(shape)
				shapeRemove			元素删除				(shape)
				shapePropChange		元素属性改变			(shape,field)
				shapePosChange		元素位置改变			(shape)
				shapeSelect			选中元素				(shape)
				shapeUnselect		取消选中				(shape)
				shapeUnselectAll	取消选中所有			()
				shapeFocus			焦点改变				(oldShape,newShape)
				canvasSizeChange	画布大小改变			(width,height)
				-------------------------------------------------------------------------------------------
			*
			* */
			templ.doc = function () {
				return {
					app:null,
                    spaceX: 100,//横向留白
                    spaceY:100,//纵向留白
					idMaker: templ.IDMaker(),
					selected: [], //所有选中的元素
					focus: null, //当前焦点
					actions: [], //action必须有函数：function event(doc,evtName,args...){}
					isDirty: true, //是否需要重绘
					view: null,
					
					downPos: null, //mousedown point
					downShape: null, //mousedown shape
					downSide: "", //mousedown side
					
					movePos: null, //mousemove point
					overShape: null, //mouse over shape

                    isMoving: false,
                    images:{},



					//==== 信息获取接口[begin] =======
					getAllShape: function () {
						return this.app.shapes;
                    },
                    getApp: function () {
                        return this.app;
                    },
					
					getFocus: function () {
						return this.focus;
					},
					getAllSelected: function () {
						return this.selected;
					},
					isSelected: function (shape) {
						var me = this;
						if (!shape) return false;
						var index = $.indexOfArray(shape, me.selected, function (a, b) {
							return a.id === b.id
						});
						return index >= 0;
					},
					getDownPos: function () {
						return this.downPos;
					},
					getDownShape: function () {
						return this.downShape;
					},
					getDownSide: function () {
						return this.downSide;
					},
					
					getMovePos: function () {
						return this.movePos;
					},
					getIsMoving: function () {
						return this.isMoving;
					},
					
					getOverShape: function () {
						return this.overShape;
					},
					
					getSize: function () {
						return canvas.size(this.app.width, this.app.height);
                    },
                    getSpace: function () {
                        return canvas.size(this.spaceX, this.spaceY);
                    },
					findShape: function (id) {
						var p = null;
						$.each(this.app.shapes, function (shape) {
							if (shape.id === id) {
								p = shape;
								return true;
							}
						});
						return p;
                    },
                    getShapeImage: function (key) {
                        var me = this;
                        
                        return me.images[key];
                    },
                    addShapeImage: function (key) {
                        var me = this;
                        if ( !(key in me.images) ) {
                            me.images[key] = null;//先占位，避免重复加载

                            var img = new Image();
                            img.onload = function () {
                                me.images[key] = img;
                                me.isDirty = true;
                                $.log("---->load image " + key);
                                me.updateView();
                            };
                            img.src = key;
                        } 
                        
                        return me.images[key];
                    },
                    removeShapeImage: function (key) {
                        var me = this;
                        if (key in me.images) delete me.images[key];
                    },
					//==== 信息获取接口[end] =======

                    //==== 文档操作[begin] =======
                    setApp: function (oApp) {
                        var me = this;
                        me.app = oApp;

						//reset ids
						var pid = me.idMaker;
						pid.id = 0; //reset

						$.each(oApp.shapes, function (p) {
							
							if (p.id > pid.id) {
								pid.id = p.id;
                            }
                            if (p.type == templ.ShapeType.IMAGE) {
                                if (p.url) {
                                    me.addShapeImage(p.url);
                                }
                            }
                        });

                        me.isDirty = true;
                        fnDocSizeChange({ width: oApp.width, height: oApp.height }, function () {
                            me.updateView();
                        });
                        
                    },
                    setDirty: function (b) {
                        this.isDirty = b;
                    },
					setFocus: function (shape) {
						var me = this;
						var focus = me.focus;
						if (focus !== shape) {
							me.focus = shape;
							me.notify("shapeFocus", focus, shape);
							me.isDirty = true;
						}
					},
					setView: function (oView) {
						this.view = oView;
					},

					setSize: function (sz) {
						var me = this;
						me.app.width = sz.width;
						me.app.height = sz.height;
						me.isDirty = true;
						fnDocSizeChange(sz, function () {
							me.updateView();
						})
                    },
                    setSpace: function (sz) {
                        this.spaceX = sz.width;
                        this.spaceY = sz.height;
                    },

					setDownPos: function (pos) {
						this.downPos = pos;
					},

					setDownShape: function (shape) {
						this.downShape = shape;
					},
					setDownSide: function (side) {
						this.downSide = side;
					},
					setOverShape: function (shape) {
						if (this.overShape !== shape) {
							this.overShape = shape;
							this.isDirty = true;
						}
					},
					
					setIsMoving: function (isMoving) {
						if (!this.isMoving) {
							this.isDirty = true;
							this.isMoving = isMoving;
						}
					},
					setMovePos: function (pos) {
						var v = this.movePos;
						if (v && pos && v.x === pos.x && v.y === pos.y) {
							//is same
						} else {
							this.movePos = pos;
							this.isDirty = true;
						}

					},
					select: function (shape) {
						var me = this;
						if (!me.isSelected(shape)) {
							me.selected.push(shape);
							me.notify("shapeSelect", shape);
							me.isDirty = true;
						}
					},
					unselect: function (shape) {
						var me = this;
						if (!shape) return;
						var index = $.indexOfArray(shape, me.selected, function (a, b) {
							return a.id === b.id
						});
						if (index >= 0) {
							me.isDirty = true;
							me.selected.splice(index, 1);
							me.notify("shapeUnselect", shape);
							if (shape === me.focus) {
								var focus = null;
								if (me.selected.length > index) {
									focus = me.selected[index];
								} else if (me.selected.length) {
									focus = me.selected[me.selected.length - 1];
								}
								me.setFocus(focus);
							}
						}
					},
					unselectAll: function () {
						var me = this;
						if (me.selected.length > 0) {
							me.isDirty = true;
							me.selected = [];
							me.setFocus(null);
							me.notify("shapeUnselectAll");
						}
					},
					moveShape: function (shape, cx, cy) {
						var me = this;
						if (!(cx === 0 && cy === 0)) {
							shape.position.x += cx;
							shape.position.y += cy;
							me.isDirty = true;
							me.notify("shapePosChange", shape);
						}

                    },
                    resizeShape: function (shape,side, cx, cy) {
						var me = this;
                        if (!(cx === 0 && cy === 0)) {
                            var p = shape.position;
                            if (side == "LeftTop") {
                                p.x += cx;
                                p.y += cy;
                                p.width -= cx;
                                p.height -= cy;
                            } else if (side == "RightTop") {
                                p.width += cx;
                                p.y += cy;
                                p.height -= cy;
                            } else if (side == "LeftBottom") {
                                p.x += cx;
                                p.width -= cx;
                                p.height += cy;
                            } else if (side == "RightBottom") {
                                p.width += cx;
                                p.height += cy;
                            }
							me.isDirty = true;
							me.notify("shapePosChange", shape);
						}

					},
					
					deleteSelected: function () {
						var me = this;
						$.each(me.selected, function (shape) {
							var index;
							index = $.indexOfArray(shape, me.app.shapes, function (a, b) {
                                return a.id === b.id;
                            });
                            if (index >= 0) me.app.shapes.splice(index, 1);
							me.notify("shapeRemove", shape);
						});
						me.isDirty = true;
						me.unselectAll();
						me.setFocus(null);
					},
					//==== 文档操作[end] =======

					//事件触发
					notify: function () {
						var me = this;
						var arr = [me];
						for (var i = 0; i < arguments.length; i++) {
							arr.push(arguments[i]);
						}

						$.each(this.actions, function (action) {
							var fn = action.event;
							if ($.isFunction(fn)) {
								fn.apply(action, arr)
							}
						})
					},
					//更新视图
					updateView: function () {
						var me = this;
						if (me.isDirty) {
							me.isDirty = false;
							if (me.view) {
								me.view.draw(me);
							}
						}
					},
					//注册action
					registerAction: function (a) {
						this.actions.push(a)
					},

					//其它工具函数
					hitTest: function (pt) {
						var me = this;
						var t = null;

						var focus = me.getFocus(0);
						if (focus) {
							t = me.hitTestShape(pt, focus);
							if (t) return t;
						}
						var selected = me.getAllSelected();
						var count = selected.length;
						var i = 0;
						for (i = 0; i < count; i++) {
							t = me.hitTestShape(pt, selected[i]);
							if (t) return t;
						}

	

						var shapes = me.getAllShape();
						count = shapes.length;
						for (i = 0; i < count; i++) {
							t = me.hitTestShape(pt, shapes[i]);
							if (t) return t;
						}

						return null;
					},
					hitTestShape: function (pt, shape) {
						var me = this;

						if (me.isSelected(shape)) {
							var hShape = me.getHandle(shape);
							if (hShape) {
								var found = null;
								$.each(hShape, function (r, k) {
									//$.log("hit on "+shape.id+" " +k+ " pt:"+$.toJson(pt)+" r="+$.toJson(r));

									if (ptInRect(pt, r)) {
										found = {
											shape: shape,
											position: k
										};
										return true;
									}
								});
								if (found) return found;
							}
						}

						var rBody = shape.position;
							if (ptInRect(pt, rBody)) {
								return {
									shape: shape,
									position: "body"
								}
							}
					},

					addShape: function (p) {
						p.id = this.idMaker.New();
                        this.app.shapes.push(p);
                        this.isDirty = true;
						return p;
					},
					
					getHandle: function (shape) {
						var me = this;
						var hw = scale(templ.HANDLE_WIDTH);

						var r = shape.position;

							return {
								LeftTop: canvas.rect(r.x - hw, r.y - hw, hw, hw),
								RightTop: canvas.rect(r.x + r.width, r.y - hw, hw, hw),
								LeftBottom: canvas.rect(r.x - hw, r.y + r.height, hw, hw),
								RightBottom: canvas.rect(r.x + r.width, r.y + r.height, hw, hw)
							}
					},
					newID: function () {
						return this.idMaker.New();
					}

				}
			};

			return templ;
		}
    }
})();